#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

/*
in_addr_t inet_addr(const char *cp);  // 将主机IP 转成 网络IP
char *inet_ntoa(struct in_addr in);   // 将网络IP 转成  主机IP
uint16_t htons(uint16_t hostshort);  //  将主机端口号 转成 网络端口号
uint16_t ntohs(uint16_t netshort);   //  将网络端口号 转成 主机端口号

建立套接字 scocket   返回sockfd
int socket(int domain, int type, int protocol);
    domain 网络协议 AF_INET    AF_INET6
    type  SOCK_STREAM 选择TCP   SOCK_DGRAM 选择UDP

绑定自己的IP地址  和 端口号   0、-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    sockfd  套接字描述符
    addr    结构体sockaddr的指针 ip和端口
    addrlen  结构体的大小

listen()声明sockfd处于监听状态  0、-1
int listen(int sockfd, int backlog);
    sockfd ： 套接字文件描述符
    backlog  ：同时  最多支持连接上来的客户端总数


等待接受客户端连接  失败-1  返回已连接的套接字文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    sockfd： 套接字文件描述符
    addr  ：  连接上来的客户端的IP地址和端口号
    addrlen ： 长度

接收数据  失败-1  返回0 表示客户端断开连接
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    sockfd： 已连接的套接字文件描述符  connectfd
    buf  ： 接收到的数据存储到这里
    len  ：  接收数据的大小
    flags  ：  一般设置为  0



发起连接 客户端发起连接  返回0  失败-1
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    sockfd  --> 套接字文件描述符
    addr   --- 【服务器的IP地址  和 端口号】
    addrlen  ---地址的长度    sizeof(struct sockaddr_in )


发送数据  返回发送的字节数  失败-1
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
*/
// 发
void *start_routine(void *sockfd)
{

    printf("子线程 参数%d 线程号%lu\n", *((int *)sockfd), pthread_self());

    while (1)
    {
        char buf[1024] = {0};
        printf("input: ");
        scanf("%s", buf);

        // 发送数据
        ssize_t send_res = send(*((int *)sockfd), buf, sizeof(buf), 0);
        if (send_res < 0)
        {
            perror("send error");
            return NULL;
        }
        printf("send ok... %ld %s\n", send_res, buf);

        // 判断退出
        if (strcmp(buf, "exit") == 0)
        {
            break;
        }
    }

    printf("线程 end\n");
    close(*((int *)sockfd));
}

int main()
{
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error");
        return -1;
    }
    printf("socket ok...\n");

    // 创建Server端地址结构体
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;                        // 使用ipv4
    addr.sin_port = htons(11111);                     // 端口   htons()  主机字节序转网络字节序host to net s16位 端口
    addr.sin_addr.s_addr = inet_addr("192.168.46.2"); // ip  inet_addr() 将主机IP 转成 网络IP
    // addr.sin_addr.s_addr = inet_addr("8.138.17.18"); // ip  inet_addr() 将主机IP 转成 网络IP

    // 绑定自己的地址和端口【忽略】

    // 建立连接
    int connect_res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if (connect_res < 0)
    {
        perror("connect error");
        return -1;
    }
    printf("connect ok...\n");

    // 创建一个线程
    pthread_t thread;
    int res = pthread_create(&thread, NULL, start_routine, &sockfd);
    printf("res=%d  thread=%lu\n", res, thread);

    // 收到
    while (1)
    {
        char buf[1024] = {0};
        int recv_res = recv(sockfd, buf, sizeof(buf), 0);
        if (recv_res < 0)
        {
            printf("recv error\n");
            break;
        }
        if (recv_res == 0)
        {
            printf("服务器断开连接\n");
            break;
        }

        // for (int i = 0; i < 10; i++)
        // {
        //     printf("%d-%c\n", buf[i], buf[i]);
        // }

        if (strcmp(buf, "exit") == 0)
        {
            break;
        }

        printf("接收到来自服务器的消息recv %s\n", buf);
    }

    // 客户端主动要求退出 ，取消接收服务器端的消息线程
    pthread_cancel(thread);

    // 阻塞等待线程结束
    pthread_join(thread, NULL);
    printf("回收线程\n");

    // 关闭套接字 放线程后面 避免 关闭套接字时线程还在接收数据 会用到套接字 野指针
    close(sockfd);
    printf("client over...\n");

    return 0;
}

// https://newstar.wiki/learn/nc  tcp调试工具
// https://blog.csdn.net/qq_24305079/article/details/147476573 简易教程
// 模拟客户端  nc 192.168.46.2 11111
// 模拟服务端  nc -l 11111
